package cuShapes;

import java.awt.Color;

import xml.*;
import java.io.*;
import javax.swing.event.EventListenerList;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.*;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.List;
import java.io.Serializable;


/*******************************************************
 * PQShape is an abstract base class for all shapes that
 * will be drawn or built for this program.  The shape consists 
 * of an array of PQLines that outline the shape, and three Rectangles
 * that are used to acknowledge when the shape is clicked by the mouse.  
 * 
 * @author Dan Ward, Chris Walling, Jonathan Dickson 
 *
 */
@XmlTransient
public abstract class PQShape implements Serializable{
	
	/**
	 * 
	 */
	@XmlTransient
	private static final long serialVersionUID = -85465013875149687L;
	// produce a wrapper XML element around this collection
	
	/********************************************************************************************
	 * A list of lines that represent the outline of the shape to be drawn.  The Lines are ordered
	 * so they start from the top left corner, and finish ending in the bottom right.  
	 */
	@XmlJavaTypeAdapter(value=LinesAdapter.class)
	@XmlElement(name = "lineslist")
	protected ArrayList<PQLine> lines;
	
	/***************************************************************************************
	 *  This is the color that the shape should be drawn in.  It is updated
	 *   by actionHandlers.ColorHandler
	 */
	@XmlJavaTypeAdapter(ColorAdapter.class)
	@XmlAttribute
	protected Color color;
	
	/***************************
	 * The name of the shape 
	 */
	@XmlAttribute(required=true)
	protected String name;
	
	/*********************************************
	 * Rectangle used to resize or move the shape 
	 */
	@XmlJavaTypeAdapter(RectangleAdapter.class)
	@XmlElement
	protected Rectangle center, resize, rect;
	
	/********************************************************
	 * The currently selected rectangle being moved, or NULL
	 */
	@XmlTransient
	protected Rectangle workingRect;
	
	/**********************************************************************
	 * Dimensions of the shape.  Most used to position rects during resizing
	 * or moving. 
	 */
	@XmlElement
	protected int width, height;
	
	/************************************************************************
	 * A action handler that listens for line draws.
	 */
	@XmlTransient
	protected EventListenerList listenerList;
	
	 /**********************************************************************
	 * Added so that XmlReader knows what shapes are connected. At any given
	 * time no two shapes should have the same id number. 
	 * Without it would have to test by name of the shape which prevents
	 * multiple shapes from having the same name. Also int compares are
	 * less expensive than string compares.
	 * 
	 */
	@XmlAttribute(required=true)
	protected int shapeId;

	/**********************************
	 * Instantiates member variables
	 * defaulting the color to Black
	 **********************************/
	protected PQShape () {
		lines = new ArrayList<PQLine>();
		color = Color.black;
		listenerList = new EventListenerList();
	}
	
	/************************************
	 * Calls the default constructor, then
	 * explicitly sets the shapeId
	 * @param id int value to set the shapeId to
	 */
	protected PQShape(int id) {
		this();
		shapeId = id;	
	}
	
	/***********************************
	 * 
	 * @return the list of lines that 
	 *    makes the shape
	 *    will check for empty list
	 ***********************************/
	public List<PQLine> getLineList() {
		if (lines == null) {
            lines = new ArrayList<PQLine>();
        }
        return this.lines;
	}
	
	/**********************************************************************
	 * Added so that XmlReader knows what shapes are connected. At any given
	 * time no two shapes should have the same id number. 
	 * Without it would have to test by name of the shape which prevents
	 * multiple shapes from having the same name. Also int compares are
	 * less expensive than string compares.
	 * 
	 * @return shapeId
	 * 
	 ************************************************************************/
	public int getId() {
		return shapeId;
	}
	
	/*************************
	 * @return the name of this class
	 *******************************/
	public String toString() {		
		return this.name;
	}
	
	/*This method is the easiest way to get a deep copy for any PQShape.  During implementation
	 * there were only two ways in which to get a deep copy.  The other method was to create a
	 * copy constructor for every class and subclass, which didn't seem worth it, especially when
	 * it comes to editing any class later.  This implementation just requires that any classes
	 * in the tree structure implement Serializable.
	 * 
	 * --Chris Walling
	 */
	/********************************************
	 * makes a deep copy of all the data in the class.
	 * 
	 * @return a copy of the shape that calls it. 
	 */
	public PQShape getDeepCopy()throws Exception{
		ObjectOutputStream oos = null;
	    ObjectInputStream ois = null;
	    try
	    {
	       ByteArrayOutputStream bos = new ByteArrayOutputStream();
	       oos = new ObjectOutputStream(bos);
	       oos.writeObject(this);
	       oos.flush();
	       ByteArrayInputStream bin = new ByteArrayInputStream(bos.toByteArray());
	       ois = new ObjectInputStream(bin);
	       return (PQShape)ois.readObject();
	    }
	    catch(Exception e)
	    {
	       System.out.println("Exception in ObjectCloner = " + e);
	       throw(e);
	    }
	    finally
	    {
	       oos.close();
	       ois.close();
	    }
	}
	/***********************************
	 *  Draws each of the lines in the 
	 *    line list, applies the name
	 *    and fills the center and resize
	 *    rects.  
	 * @param g2 the graphic we are drawing with
	 */
	public void drawToCanvas(Graphics2D g2) {
		for (PQLine line : lines) {
			line.drawLine(g2);
		}

		g2.drawString(name, center.x-14, resize.y-10);
		//g2.setColor(new Color(255,255,255));
		//g2.fill(rect);
		g2.setColor(new Color(0,0,0,75));
		g2.fill(center);
		g2.fill(resize);
	}
	
	public void setName(String s) {
		name = s;
	}
	

	
	/* Getters */
	public Color 		getColor(){ 		return this.color; }
	public String 		getName(){ 			return this.name; }
	public Rectangle 	getRect(){			return this.rect;	}
	public Rectangle 	getCenterRect(){	return this.center;	}
	public Rectangle	getResizeRect() {	return this.resize; }
	public Rectangle	getWorkingRect(){	return this.workingRect; }
    public Point 		getStart() {		return lines.get(0).getStart();	}   
    public Point 		getEnd() {			return lines.get(0).getEnd();	}
	
	/* Setters */
	public void setWorkingRect(Rectangle r){ workingRect = r; }
		
	/*  Abstract Methods */
    public void setStart(Point start) {
    }
    public void setEnd(Point end) {
    }
    
    
    /**********************************************
     * Changes the location of the workingRect.  The
     * dimensions of the shape will be updated.   
     * @param p Point to move topleft of workingRect to
     */
	public abstract void moveWorkingRect(Point p);
	
	/****************************************
	 * hit rect returns true if the given point 
	 * falls inside of any of the shape's rects.
	 * 
	 *  In the case of a hit, hitRect will set 
	 *  the workingRect to the rect colliding the point. 
	 *  
	 * @param p a point to look for a rect
	 * @return boolean true if we hit, false otherwise
	 */
	public abstract boolean hitRect(Point p);
    
    public abstract int getWidth();
    public abstract int getHeight();

    public abstract String getShapeClass();
    
    public int getShapeId() {
    	return shapeId;
    }
    
    /********************************************************
     *   This method should only be used by ConnectableAdapter 
     *   so that the integrity of the drawn shapes remain
     * @param id Int to set the shapeId to
     */
    public void setShapeId(int id) {
    	shapeId = id;
    }
    
    public abstract void reAddConnections();
    
    	
}
